/**
 * 20.1.9：更改copy为THREE like，移除不必要的clone重写
 * 21.4.8：增加中心偏移offset属性（更新raycast为sprite类似方法----暂停）
 * 210819：升级修改（three.js r120-r131）
 */
let THREE=require('three');
import Props from '../general/props';

const _zero=new THREE.Vector3(0,0,0);

/**
 * 始终面向相机的平面
 @param values {object}
 size:0.5, 尺寸大小
 opacity:1, 不透明度
 通过material编辑通用材质属性
 通过geometry编辑通用几何属性
 */
export default class VirtualPlane extends THREE.Object3D{
    constructor(values={}){
        super();
        console.warn('this class is deprecated now!');
        this.isMesh=true;
        this.drawMode=THREE.TrianglesDrawMode;
        this.geometry=new THREE.BufferGeometry();
        this.material=new THREE.ShaderMaterial();
        this._values=values;
        this._init();
    }

    //---------------------------------对外属性及方法--------------------------------------

    /**
     * 获取属性
     * @returns {props}
     */
    get props(){
        return this._props;
    }

    /**
     * 设置属性
     * @param values
     */
    set props(values){
        for(let key in values){
            if(this._props[''+key+'']){
                this._props[''+key+''].value=values[''+key+''];
            }else{
                console.warn(''+key+' is not a prop of VirtualPlane!');
            }
        }
    }

    /**
     * 尺寸
     */
    get size(){
        return this._props.size.value;
    }
    set size(num){
        this._props.size.value=num;
    }

    /**
     * 不透明度
     */
    get opacity(){
        return this._props.opacity.value;
    }
    set opacity(num){
        this._props.opacity.value=num;
    }

    /**
     * 中心偏移
     */
    get offset(){
        return this._props.offset.value;
    }
    set offset(vec2){
        this._props.offset.value=vec2;
    }

    /**
     * 获取包围球
     * @returns {null|Sphere}
     */
    get boundingSphere(){
        return this._boundingSphere;
    }

    /**
     * 获取顶点着色器
     * @returns {string}
     */
    get vertexShader(){
        return [
            'attribute vec2 direction;' ,
            'uniform float size;',
            'uniform vec2 offset;',
            'varying vec2 vuv;',
            'float computeAspect(){' ,
            '   float aspect=0.5625;' ,
            '   if(projectionMatrix[3][3]==0.0){' ,
            '      float _tan=projectionMatrix[1][1];' ,
            '      float as_tan=1.0/projectionMatrix[0][0];' ,
            '      aspect=1.0/(as_tan*_tan);' ,
            '   }else{' ,
            '      float _height=1.0/projectionMatrix[1][1];' ,
            '      float _width=1.0/projectionMatrix[0][0];' ,
            '      aspect=_height/_width;' ,
            '   }' ,
            '   return aspect;' ,
            '}',
            'void main(){' ,
            '   vec4 mvPosition=modelViewMatrix*vec4(position,1.);' ,
            '   vec4 pmvPosition=projectionMatrix*mvPosition;' ,
            '   vec3 dv=vec3(direction,0.);' ,
            '   float aspect=computeAspect();' ,
            '   dv.x*=aspect;' ,

            '   dv=normalize(dv);' ,
            '   dv.x+=abs(dv.x)*offset.x;' ,
            '   dv.y+=abs(dv.y)*offset.y;' ,
            '   dv*=size;' ,
            '   gl_Position=vec4(pmvPosition.xyz+dv,pmvPosition.w);' ,
            '   vuv=uv;',
            '}'
        ].join('\n');
    }

    /**
     * 获取片元着色器
     * @returns {string}
     */
    get fragmentShader(){
        return [
            '#define PI 3.1415926' ,
            'uniform float opacity;' ,
            'varying vec2 vuv;' ,
            'void main(){' ,
            '   vec3 col=vec3(.3,1.,0.);' ,
            '   vec2 tuv=(vuv-.5);' ,
            '   float dis=length(tuv);' ,
            '   float md=mod(dis,0.17)*10.;' ,
            '   float ad=0.5-dis;' ,
            '   float alpha=ad*md+ad;' ,
            '   gl_FragColor=vec4(col,alpha*opacity);' ,
            '}'
        ].join('\n');
    }

    get center(){
        this._center.copy(this._props.offset.value);
        this._center.multiplyScalar(0.5);
        this._center.x+=0.5;
        this._center.y+=0.5;
        return this._center;
    }

    /**
     * 计算包围球
     */
    computeBoundingSphere(){
        this._boundingSphere=new THREE.Sphere(_zero,this._props.size.value);
        this.geometry.boundingSphere=this._boundingSphere;
    }

    /**
     * 销毁
     */
    dispose(){
        this.geometry.dispose();
        if(this.material.map){
            this.material.map.dispose();
        }
        this.material.dispose();
        this._values=null;
        this._center=null;
        this._boundingSphere=null;
        this._props.dispose();
        this._props=null;
    }

    /**
     * 复制
     * @param source
     * @param recursive {boolean}
     */
    copy(source,recursive=false){
        super.copy(source,recursive);
        this.material.copy(source['material']);
        this.material.needsUpdate=true;
        this.props=source['props'].all(true);
        this.computeBoundingSphere();
        return this;
    }

    //----------------------------------------私有属性及方法----------------------------------

    _init(){
        this._initProps();
        this._initVariable();
        this._initComponents();
        this._initPropCallback();
    }
    _initProps(){
        this._props=new Props({
            size:this._values.size||0.5,
            opacity:this._values.opacity===undefined?1.:this._values.opacity,
            offset:this._values.offset||new THREE.Vector2(0,0),
        });
    }
    _initPropCallback(){
        this._props.onKeyChange('size',(_new,_old)=>{
            this.computeBoundingSphere();
            this.material.uniforms.size.value=_new;
        });
        this._props.onKeyChange('opacity',(_new,_old)=>{
            this.material.uniforms.opacity.value=_new;
        });
        this._props.onKeyChange('offset',(_new,_old)=>{
            this.material.uniforms.offset.value.copy(_new);
        });
    }
    _initVariable(){
        this._center=new THREE.Vector2();
        this._boundingSphere=null;
    }
    _initComponents(){
        this.computeBoundingSphere();
        this._initGeometry();
        this._initMaterial();
        this._initRaycast();
    }
    _initGeometry(){
        let ps=new Float32Array([0,0,0,  0,0,0,  0,0,0,  0,0,0]);
        let us=new Float32Array([0,0,    0,1,    1,1,    1,0]);
        let ds=new Float32Array([-1,-1,  -1,1,   1,1,    1,-1]);
        this.geometry.setIndex([0,2,1,0,3,2]);
        this.geometry.setAttribute('position',new THREE.BufferAttribute(ps,3));
        this.geometry.setAttribute('uv',new THREE.BufferAttribute(us,2));
        this.geometry.setAttribute('direction',new THREE.BufferAttribute(ds,2));
    }
    _initMaterial(){
        this.material.vertexShader=this.vertexShader;
        this.material.fragmentShader=this.fragmentShader;
        this.material.uniforms={
            size:{value:this._props.size.value},
            opacity:{value:this._props.opacity.value},
            offset:{value:this._props.offset.value},
        };
        this.material.depthWrite=false;
        this.material.transparent=true;
        this.material.blending=THREE.AdditiveBlending;
    }
    _initRaycast(){
        this.raycast=(()=>{
            let inverseMatrix = new THREE.Matrix4();
            let ray = new THREE.Ray();
            let sphere = new THREE.Sphere();
            return (raycaster,intersects)=>{
                let object = this;
                let geometry = this.geometry;
                let matrixWorld = this.matrixWorld;
                let threshold = this.props.size.value*0.3;//raycaster.params.Points.threshold;
                if ( this.boundingSphere === null ) this.computeBoundingSphere();
                sphere.copy( this.boundingSphere );
                sphere.applyMatrix4( matrixWorld );
                sphere.radius += threshold;
                if ( raycaster.ray.intersectsSphere( sphere ) === false ) return;

                // inverseMatrix.getInverse( matrixWorld );
                inverseMatrix.copy(matrixWorld).invert();//210819

                ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix );
                let localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 );
                let localThresholdSq = localThreshold * localThreshold;
                let position = new THREE.Vector3();
                let testPoint=( point, index )=> {
                    let rayPointDistanceSq = ray.distanceSqToPoint( point );
                    if ( rayPointDistanceSq < localThresholdSq ) {
                        let intersectPoint =new THREE.Vector3();
                        ray.closestPointToPoint( point ,intersectPoint);
                        intersectPoint.applyMatrix4( matrixWorld );
                        let distance = raycaster.ray.origin.distanceTo( intersectPoint );
                        if ( distance < raycaster.near || distance > raycaster.far ) return;
                        intersects.push( {
                            distance: distance,
                            distanceToRay: Math.sqrt( rayPointDistanceSq ),
                            point: intersectPoint.clone(),
                            index: index,
                            face: null,
                            object: object
                        } );
                    }
                };
                if ( geometry.isBufferGeometry ) {
                    let positions =geometry.getAttribute('position').array;
                    for (let i = 0, l = positions.length / 3; i < l; i++) {
                        position.fromArray(positions, i * 3);
                        testPoint(position, i);
                    }
                }
            };
        })();
    }
}
